#pragma once
#include "../Ode.h"

namespace Skill
{
	namespace Ode
	{	
		namespace Geometries
		{
			ref class DGeometry;
		}
	}
}
using namespace Skill::Ode::Geometries;
namespace Skill
{
	namespace Ode
	{	
		ref class DAabb;
		ref class DSpace;		

		/// <summary>
		/// brief User callback for geom-geom collision testing.
		/// </summary>
		/// <param name = "data"> The user data object, as passed to dSpaceCollide. </param>
		/// <param name = "o1"> The first geom being tested. </param>
		/// <param name = "o2"> The second geom being test. </param>		
		/// <remarks> The callback function can call dCollide on o1 and o2 to generate
		/// contact points between each pair. Then these contact points may be added
		/// to the simulation as contact joints. The user's callback function can of
		/// course chose not to call dCollide for any pair, e.g. if the user decides
		/// that those pairs should not interact.
		/// ingroup collide
		/// </remarks>		
		public delegate void DNearCallback(Object^ data, DSpace^ o1, DSpace^ o2);

		public ref class DSpace abstract : DIRegisteredObject
		{
			INTERNAL_CLASS_DECLARATION_WITH_REGISTER_NO_FINALIZE(DSpace,void);
		protected:
			DSpace()
			{
				_Spaces = gcnew Dictionary<GeomKey,DSpace^>();
				NearCallback = nullptr;
			}
		private:
			Dictionary<GeomKey,DSpace^>^ _Spaces;
		internal:
			void AddSpace(DSpace^ s);
			void RemoveSpace(DSpace^ s);			
			Object^ _CurrenData;			
			inline dSpaceID _Space() { return (dSpaceID)_This; }
		public:
			UNMANAGEDPOINTER_DECLARATION();
			REGISTEREDOBJECT_DECLARATION();

			!DSpace();

			VALUE_PROP_DECLARATION_GETSET(int,CleanupMode);

			DNearCallback^ NearCallback;

			///<summary>
			/// Determing if a space is a geometry.
			///</summary>
			virtual property bool IsSpace
			{
				bool get(){return true;}
			}

			///<summary>
			/// Gets or set name of space
			///</summary>
			virtual REF_PROP_DECLARATION_GETSET(String,Name);

			///<summary>
			/// Gets or Set the user-defined data stored in the space.
			///</summary>
			REF_PROP_DECLARATION_GETSET(Object,UserData);

			/// <summary>
			/// brief Sets sublevel value for a space.			
			/// Sublevel affects how the space is handled in dSpaceCollide2 when it is collided
			/// with another space. If sublevels of both spaces match, the function iterates 
			/// geometries of both spaces and collides them with each other. If sublevel of one
			/// space is greater than the sublevel of another one, only the geometries of the 
			/// space with greater sublevel are iterated, another space is passed into 
			/// collision callback as a geometry itself. By default all the spaces are assigned
			/// zero sublevel.			
			/// note
			/// The space sublevel @e IS @e NOT automatically updated when one space is inserted
			/// into another or removed from one. It is a client's responsibility to update sublevel
			/// value if necessary.						
			/// ingroup collide
			/// see dSpaceGetSublevel
			/// see dSpaceCollide2
			/// </summary>			
			VALUE_PROP_DECLARATION_GETSET(int,Sublevel);

			void Add(DGeometry^ geom);
			void Remove(DGeometry^ geom);
			int Query(DGeometry^ geom);

			void Clean();
			/// <summary>
			/// Clean space and dispose all internal geometries
			/// </summary>
			void CleanAndDispose();

			VALUE_PROP_DECLARATION_GET(int,NumGeoms);

			property DGeometry^ default[int]
			{
				DGeometry^ get(int i);
			}

			///<summary>
			/// brief Given a space, this returns its class.
			/// The ODE classes are:
			/// @li dSimpleSpaceClass
			/// @li dHashSpaceClass
			/// @li dSweepAndPruneSpaceClass
			/// @li dQuadTreeSpaceClass
			/// @li dFirstUserClass
			/// @li dLastUserClass 
			/// The class id not defined by the user should be between
			/// dFirstSpaceClass and dLastSpaceClass.
			/// User-defined class will return their own number.
			///</summary>
			virtual VALUE_PROP_DECLARATION_GET(int,ClassID);	

			///<summary>
			/// Retrieves the axis-aligned bounding box that surrounds all contained geoms				
			///</summary>
			virtual REF_PROP_DECLARATION_GET(DAabb,Aabb);

			///<summary>
			/// The containing space.
			///</summary>
			///<returns>The space that contains the geom, or NULL if the geom is not contained by a space. </returns>				
			REF_PROP_DECLARATION_GET(DSpace,Container);


			///<summary>
			/// Determines which pairs of geoms in a space may potentially intersect,
			/// and calls the callback function for each candidate pair.
			///</summary>
			/// <param name = "data">
			///	data Passed from Collide directly to the callback
			///	function. Its meaning is user defined. The o1 and o2 arguments are the
			///	geoms that may be near each other.
			///</param>
			///<remarks>
			///	Other spaces that are contained within the colliding space are
			/// not treated specially, i.e. they are not recursed into. The callback
			/// function may be passed these contained spaces as one or both geom
			/// arguments.
			///
			/// Collide() is guaranteed to pass all intersecting geom
			/// pairs to the callback function, but may also pass close but
			/// non-intersecting pairs. The number of these calls depends on the
			/// internal algorithms used by the space. Thus you should not expect
			/// that dCollide will return contacts for every pair passed to the
			/// callback.				
			///</remarks>
			void Collide(Object^ data);

			///<summary>
			/// Determines which geoms from one space may potentially intersect with 
			/// geoms from another space, and calls the callback function for each candidate 
			/// pair. 
			///</summary>
			/// <param name = "space2"> The second space to test. </param>
			/// <param name = "data">
			///	data Passed from Collide directly to the callback
			///	function. Its meaning is user defined. The o1 and o2 arguments are the
			///	geoms that may be near each other.
			///</param>
			///<remarks>
			///This function can also test a single non-space geom against a 
			/// space. This function is useful when there is a collision hierarchy, i.e. 
			/// when there are spaces that contain other spaces.
			///
			/// Other spaces that are contained within the colliding space are
			/// not treated specially, i.e. they are not recursed into. The callback
			/// function may be passed these contained spaces as one or both geom
			/// arguments.
			///
			/// Sublevel value of space affects how the spaces are iterated.
			/// Both spaces are recursed only if their sublevels match. Otherwise, only
			/// the space with greater sublevel is recursed and the one with lesser sublevel
			/// is used as a geom itself.
			///
			/// Collide() is guaranteed to pass all intersecting geom
			/// pairs to the callback function, but may also pass close but
			/// non-intersecting pairs. The number of these calls depends on the
			/// internal algorithms used by the space. Thus you should not expect
			/// that dCollide will return contacts for every pair passed to the
			/// callback.
			///</remarks>
			void Collide(DSpace^ space2, Object^ data);
		};		
	}
}